/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2011.10.11                                          *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/bdry/NeumannBoundaryCondition.h"

//LIBMESH headers

//COMMON headers
#include "COMMON/equa/PDEEquationEnsembleStrategy.h"
#include "COMMON/equa/EquationDomain.h"
#include "COMMON/info/InfoManager.h"
#include "COMMON/info/CommonInfo.h"
//#include "COMMON/math/Function.h"
#include "COMMON/tbox/InputDatabase.h"
#include "COMMON/tbox/Utilities.h"
#include "COMMON/tbox/LogMessage.h"
#include "COMMON/enum/FunctionForm.h"


//#include "gFreeMAPs/info/ParaFluxBean.h"


namespace FreeMAPs
{

NeumannBoundaryCondition::NeumannBoundaryCondition (
        const std::string& parent_name,
        const std::string& bc_name,
        const PDEEquationEnsembleStrategy* equation_ensemble):
        Parent (parent_name, bc_name, equation_ensemble),
        d_para_flux (0),
        d_coef_absorption("1"),
        d_direction_type (DirectionType::ISOTROPIC),
        //d_direction (0.0, 0.0, 0.0),
        d_is_back_direction_allowable (false)
{
    //d_para_flux = new gFreeMAPs::ParaFluxBean();
}



NeumannBoundaryCondition::~NeumannBoundaryCondition()
{
    this->clear();
}



void
NeumannBoundaryCondition::clear()
{
    //Utilities::clear_pointer_object (d_coef_absorption);
}



void
NeumannBoundaryCondition::set_para_flux(
        gFreeMAPs::ParaFluxBean* para_flux)
{
    d_para_flux = para_flux;
}



gFreeMAPs::ParaFluxBean*
NeumannBoundaryCondition::get_para_flux()
{
    return d_para_flux;
}



void
NeumannBoundaryCondition::set_coef_absorption(
        const std::string& coef_absorption)
{
    d_coef_absorption = coef_absorption;
}



const std::string&
NeumannBoundaryCondition::get_coef_absorption()
{
    return d_coef_absorption;
}



void
NeumannBoundaryCondition::set_direction_type(
        const DirectionType& type)
{
    d_direction_type = type;
}



const DirectionType&
NeumannBoundaryCondition::get_direction_type()
{
    return d_direction_type;
}



void
NeumannBoundaryCondition::set_direction(
        const double& x,
        const double& y,
        const double& z)
{
    //d_direction[0] = x;
    //d_direction[1] = y;
    //d_direction[2] = z;
}



const double
NeumannBoundaryCondition::get_direction_x()
{
    //return d_direction[0];
}



const double
NeumannBoundaryCondition::get_direction_y()
{
   // return d_direction[1];
}



const double
NeumannBoundaryCondition::get_direction_z()
{
    //return d_direction[2];
}



void
NeumannBoundaryCondition::get_info_for_derived_object_from_input (
        const InputDatabase* db)
{
    this->prepare_for_use (db);

    db->insert_allowable_key ("para_flux");
    db->insert_allowable_key ("coef_absorption");
    db->insert_allowable_key ("coef_name_of_absorption");
    db->insert_allowable_key ("coef_mode_of_absorption");
    db->insert_allowable_key ("direction");
    db->insert_allowable_key ("is_back_direction_allowable");

    db->check_allowable_keys();

#if 0
    /*
     * build para_flux
     */
    {
        d_para_flux = new Function (this->get_full_name(), "para_flux");
        db->check_requisite_key ("para_flux", DataType::UNKNOWN, false, false);
        d_para_flux->get_from_input (
            db,
            "para_flux",
            InfoManager::get_instance()->get_variable_info(),
            InfoManager::get_instance()->get_function_info());

        d_para_flux->attach_region (this->get_equation_ensemble()->get_equation_domain()->get_region());
        d_para_flux->set_used_flag();
    }

    //para_flux
    {
        //db->check_requisite_key ("para_flux", DataType::UNKNOWN, false, false);

        if( db->key_exists("para_flux") )
        {
            if(db->is_double("para_flux"))
            {
                d_para_flux->d_para_flux_type = FreeMAPs::FunctionForm::NUMERIC;

                std::stringstream ss_para_flux;
                ss_para_flux << db->get_double("para_flux");
                d_para_flux->d_para_flux_string = ss_para_flux.str();
            }
            else if(db->is_string("para_flux"))
            {
                d_para_flux->d_para_flux_type = FreeMAPs::FunctionForm::ANALYTIC;
                d_para_flux->d_para_flux_string = db->get_string("para_flux");
            }
            else if(db->is_database("para_flux"))
            {
                d_para_flux->d_para_flux_type = FreeMAPs::FunctionForm::FILE_LIST;

                const InputDatabase* db_para_flux = db->get_database("para_flux");
                if(db_para_flux->key_exists("file_list") && db_para_flux->is_database("file_list"))
                {
                    const InputDatabase* db_file_list = db_para_flux->get_database("file_list");

                    //if(db_file_list->key_exists("file_location") && db_file_list->is_string("file_location"))
                    //{
                    //    d_para_flux->d_file_location = db_file_list->get_string("file_location");
                    //}
                    if(db_file_list->key_exists("file_prefix") && db_file_list->is_string("file_prefix"))
                    {
                        const std::string& file_prefix = db_file_list->get_string("file_prefix");;

                        //d_para_flux->d_file_prefix = db_file_list->get_string("file_prefix");

                        if( file_prefix.empty() )
                        {
                            d_para_flux->d_file_location = InfoManager::get_instance()->get_common_info()->get_proj_directory();
                            d_para_flux->d_file_prefix = "L";
                        }
                        else
                        {
                            const int& pos = file_prefix.find_last_of("/");
                            if(  pos == -1 )
                            {
                                d_para_flux->d_file_location = file_prefix;
                                d_para_flux->d_file_prefix = "L";
                            }
                            else
                            {
                                d_para_flux->d_file_location = file_prefix.substr(0, pos);
                                if(pos == (file_prefix.size() - 1) )
                                {
                                    d_para_flux->d_file_prefix = "L";
                                }
                                else
                                {
                                    d_para_flux->d_file_prefix = file_prefix.substr(pos+1);
                                }
                                std::cout << __FILE__ << __LINE__ << " para_flux : d_para_flux->d_file_location = " << d_para_flux->d_file_location << std::endl;
                                std::cout << __FILE__ << __LINE__ << " para_flux : d_para_flux->d_file_prefix = " << d_para_flux->d_file_prefix << std::endl;
                            }
                        }
                    }
                    if(db_file_list->key_exists("file_postfix") && db_file_list->is_string("file_postfix"))
                    {
                        d_para_flux->d_file_postfix = db_file_list->get_string("file_postfix");
                        if(d_para_flux->d_file_postfix.empty() || d_para_flux->d_file_postfix == ".")
                        {
                            d_para_flux->d_file_postfix = "txt";
                        }
                        else if(d_para_flux->d_file_postfix.find_first_of(".") == 0)
                        {
                            d_para_flux->d_file_postfix = d_para_flux->d_file_postfix.erase(0, 1);
                        }
                    }
                    if(db_file_list->key_exists("file_index_start") && db_file_list->is_double("file_index_start"))
                    {
                        d_para_flux->d_file_index_start = db_file_list->get_double("file_index_start");
                    }
                    if(db_file_list->key_exists("file_index_end") && db_file_list->is_double("file_index_end"))
                    {
                        d_para_flux->d_file_index_end = db_file_list->get_double("file_index_end");
                    }
                    if(db_file_list->key_exists("file_index_step") && db_file_list->is_double("file_index_step"))
                    {
                        d_para_flux->d_file_index_step = db_file_list->get_double("file_index_step");
                    }
                    if(db_file_list->key_exists("time_start") && db_file_list->is_double("time_start"))
                    {
                        d_para_flux->d_time_start = db_file_list->get_double("time_start");
                    }
                    if(db_file_list->key_exists("time_end") && db_file_list->is_double("time_end"))
                    {
                        d_para_flux->d_time_end = db_file_list->get_double("time_end");
                    }
                    //std::cout << __FILE__ << __LINE__ << " d_file_location = " << d_para_flux->d_file_location << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_file_prefix = " << d_para_flux->d_file_prefix << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_file_postfix = " << d_para_flux->d_file_postfix << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_file_index_start = " << d_para_flux->d_file_index_start << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_file_index_end = " << d_para_flux->d_file_index_end << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_file_index_step = " << d_para_flux->d_file_index_step << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_time_start = " << d_para_flux->d_time_start << std::endl;
                    //std::cout << __FILE__ << __LINE__ << " d_time_end = " << d_para_flux->d_time_end << std::endl;
                }

            }
        }

    }

    {
        if(db->key_exists("coef_absorption") && db->is_double("coef_absorption"))
        {
            //double to string
            std::stringstream ss_coef_absorption;
            ss_coef_absorption << db->get_double("coef_absorption");
            d_coef_absorption = ss_coef_absorption.str();
        }
    }
    #endif
#if 0
     /*
      * build coef_absorption
      */
    {
        db->check_either_or_keys ("coef_absorption", "coef_name_of_absorption");
        if (db->key_exists ("coef_absorption"))
        {
            d_use_absp_coef_in_attr = false;
            d_coef_absorption = new Function (this->get_full_name(), "coef_absorption");
            db->check_requisite_key ("coef_absorption", DataType::UNKNOWN, false, false);
            d_coef_absorption->get_from_input (
                db,
                "coef_absorption",
                InfoManager::get_instance()->get_variable_info(),
                InfoManager::get_instance()->get_function_info());

            d_coef_absorption->attach_region (this->get_equation_ensemble()->get_equation_domain()->get_region());
            d_coef_absorption->set_used_flag();
        }
        else
        {
            d_use_absp_coef_in_attr = true;
            db->check_requisite_key ("coef_name_of_absorption", DataType::STRING, false, false);
            db->check_optional_key ("coef_mode_of_absorption", DataType::STRING, true, false);
            d_coef_name_of_absorption = db->get_string ("coef_name_of_absorption");
            d_coef_mode_of_absorption = db->get_string_with_default ("coef_mode_of_absorption", FREEMAPS_DEFAULT_STRING_VALUE);

            /*
             * now check whether the coef_name_of_absorption is exists.
             */
            const Region* region = this->get_equation_ensemble()->get_equation_domain()->get_region();

            const std::map<std::string, SimpleRegion*>& simple_regions = region->get_simple_regions();
            for (std::map<std::string, SimpleRegion*>::const_iterator pos = simple_regions.begin();
                    pos != simple_regions.end(); ++pos)
            {
                const Attribute* attr = pos->second->get_attribute();

                if (!attr->has_coefficient (d_coef_name_of_absorption))
                {
                    std::ostringstream oss;
                    oss << "in attribute \"" << attr->get_attribute_name() << "\", no coefficient named \"" << d_coef_name_of_absorption << "\"";
                    LogMessage::use_improper_string_value (
                        db,
                        "coef_name_of_absorption",
                        d_coef_name_of_absorption,
                        oss.str(),
                        attr->get_coefficient_names());
                }

                if (!attr->has_coefficient_mode (d_coef_name_of_absorption, d_coef_mode_of_absorption))
                {
                    std::ostringstream oss;
                    oss << "in attribute \"" << attr->get_attribute_name() << "\", the coefficient \"" << d_coef_name_of_absorption << "\"\nhas no mode named \"" << d_coef_name_of_absorption << "\"";
                    LogMessage::use_improper_string_value (
                        db,
                        "coef_mode_of_absorption",
                        d_coef_mode_of_absorption,
                        oss.str(),
                        attr->get_mode_names (d_coef_name_of_absorption));
                }

                attr->set_coefficient_used_flag (d_coef_name_of_absorption);
            }
        }
    }



    /*
     * build direction type
     */
    {
        db->check_optional_key ("direction", DataType::DOUBLE, false, true);
        db->check_vec_size_of_dim ("direction");

        if (db->key_exists ("direction") && db->is_double("direction"))
        {
            d_direction_type = DirectionType::COLLIMATION;

            std::vector<double> dir = db->get_double_vector ("direction");
            int dim = InfoManager::get_instance()->get_common_info()->get_dim();

            //if user change dimmension = 500, here will have bug
            if(dir.size() < dim)
            {
                dim = dir.size();
            }

            //if user change ...
            if(dim > 3 )
            {
                dim = 3;
            }


            for (int i = 0; i < dim; ++i)
            {
                d_direction (i) = dir[i];
            }

            //d_direction.normalize();

            if (db->key_exists ("is_back_direction_allowable") && db->is_bool("is_back_direction_allowable"))
            {
                db->check_optional_key ("is_back_direction_allowable", DataType::BOOL, true, false);
                d_is_back_direction_allowable = db->get_bool_with_default ("is_back_direction_allowable", d_is_back_direction_allowable);
            }
        }
        else
        {
            d_direction_type = DirectionType::ISOTROPIC;
        }
    }
    #endif
}



std::string
NeumannBoundaryCondition::get_info_from_derived_object() const
{
#if 0
    std::ostringstream os, oss;

    os << "physical_parameters\n{\n";

    //const FreeMAPs::FunctionForm& para_flux_type = d_para_flux->d_para_flux_type;

    if( para_flux_type == FreeMAPs::FunctionForm::NUMERIC )
    {
        oss << "para_flux = " << d_para_flux->d_para_flux_string << "\n";
    }
    else if(d_para_flux->d_para_flux_type == FreeMAPs::FunctionForm::ANALYTIC)
    {
        oss << "para_flux = \"" << d_para_flux->d_para_flux_string << "\"\n";
    }
    else if(d_para_flux->d_para_flux_type == FreeMAPs::FunctionForm::FILE_LIST)
    {
        oss << "para_flux\n{\n";
        oss << LogMessage::get_prefix (ONE_TAB) << "file_list\n";
        oss << LogMessage::get_prefix (ONE_TAB) << "{\n";

        //file_prefix
        //oss << LogMessage::get_prefix (TWO_TAB) << "file_location    = " << "\""<< d_para_flux->d_file_location <<"\"" << std::endl;
        //oss << LogMessage::get_prefix (TWO_TAB) << "file_prefix      = " << "\""<< d_para_flux->d_file_prefix <<"\"" << std::endl;

        if(d_para_flux->d_file_prefix.empty())
        {
            d_para_flux->d_file_prefix = "L";
        }
        const std::string& file_prefix = d_para_flux->d_file_location + "/" + d_para_flux->d_file_prefix;

        oss << LogMessage::get_prefix (TWO_TAB) << "file_prefix     = " << "\""<< file_prefix <<"\"" << std::endl;

        if(d_para_flux->d_file_postfix.empty())
        {
            d_para_flux->d_file_postfix = ".txt";
        }
        oss << LogMessage::get_prefix (TWO_TAB) << "file_postfix     = " << "\""<< d_para_flux->d_file_postfix <<"\"" << std::endl;
        oss << LogMessage::get_prefix (TWO_TAB) << "file_index_start = " << d_para_flux->d_file_index_start << std::endl;
        oss << LogMessage::get_prefix (TWO_TAB) << "file_index_end   = " << d_para_flux->d_file_index_end << std::endl;
        oss << LogMessage::get_prefix (TWO_TAB) << "file_index_step  = " << d_para_flux->d_file_index_step << std::endl;
        oss << LogMessage::get_prefix (TWO_TAB) << "time_start       = " << d_para_flux->d_time_start << std::endl;
        oss << LogMessage::get_prefix (TWO_TAB) << "time_end         = " << d_para_flux->d_time_end << std::endl;
        oss << LogMessage::get_prefix (ONE_TAB) << "}\n";
        oss << "}\n";
    }

    //oss << "coef_absorption = \"" << d_coef_absorption << "\"";
    oss << "coef_absorption = " << d_coef_absorption << "";

    if (d_direction_type == DirectionType::COLLIMATION)
    {
        oss << '\n';
        oss << "direction = " << d_direction << '\n';
        oss << "is_back_direction_allowable = " << (d_is_back_direction_allowable ? "TRUE" : "FALSE");
    }
    os << LogMessage::get_log_info (oss, ONE_TAB) << '\n';
    os << "}";

    return os.str();
#endif
}

} // namespace FreeMAPs
